/**
 * Class: ListenerManager
 *
 * Purpose: 
 *
 * Created: Jan 24, 2010 @ 1:19:13 PM
 * @author Ken Scott
 */
package org.openfantasia.util.listener;

import org.openfantasia.util.Logger;

import java.io.File;
import java.io.IOException;
import java.util.*;

public class ListenerManager {

	private enum Mode {

		NORMAL, RECORDING, PLAYBACK
	}

	private final Map<String, List<Listener>> map = new HashMap<String, List<Listener>>();

	private EventFile eventFile;

	private List<Event> eventList;

	private Mode mode = Mode.NORMAL;


	public ListenerManager() {
	}


	public boolean isRecording() {
		return mode == Mode.RECORDING;
	}


	public boolean isPlayback() {
		return mode == Mode.PLAYBACK;
	}


	public void startRecording(File file) throws IOException {
		if (file.exists()) {
			file.delete();
		}
		this.eventFile = new EventFile(file);
		this.eventFile.beginRecording();
		mode = Mode.RECORDING;
	}


	public void startRecording(String path) throws IOException {
		startRecording(new File(path));
	}


	public void stopRecording() throws IOException {
		if (this.eventFile != null) {
			this.eventFile.close();
			this.eventFile = null;
			mode = Mode.NORMAL;
		}
	}


	public boolean isPlaybackComplete() {
		Logger.log("Events remaining: " + eventList.size());
		return eventList.isEmpty();
	}


	public void playback(File file) throws IOException {
		eventFile = new EventFile(file);
		eventFile.beginPlayback();
		mode = Mode.PLAYBACK;

		eventList = new LinkedList<Event>();
		Event msg = eventFile.nextEvent();
		while (msg != null) {
			eventList.add(msg);
			msg = eventFile.nextEvent();
		}
		eventFile.close();
	}


	public void playback(double minTime, double maxTime) {
		List<Event> playable = new ArrayList<Event>();
		for (Event event : eventList) {
			double time = event.getTime();
			if ((time >= minTime) && (time <= maxTime)) {
				playable.add(event);
			}
		}

		// Play the ones that are active, and then remove them
		for (Event event : playable) {
			play(event);
			eventList.remove(event);
		}
	}


	public void play(Event event) {
		List<Listener> listeners = map.get(event.getChannel());
		if (listeners != null) {
			for (Listener listener : listeners) {
				listener.receive(event);
			}
		}
	}


	public void add(String channel, Listener listener) {
		synchronized (map) {
			List<Listener> listeners = map.get(channel);
			if (listeners == null) {
				listeners = new ArrayList<Listener>();
				map.put(channel, listeners);
			}
			if (!listeners.contains(listener)) {
				listeners.add(listener);
			}
		}
	}


	public void remove(String channel, Listener listener) {
		synchronized (map) {
			List<Listener> listeners = map.get(channel);
			if (listeners != null) {
				listeners.remove(listener);
			}
		}
	}


	public void send(String channel, Event event) {
		event.setChannel(channel);
		List<Listener> listeners = map.get(channel);
		if (listeners != null) {
			for (Listener listener : listeners) {
				listener.receive(event);
			}

			if ((mode == Mode.RECORDING) && (this.eventFile != null)) {
				this.eventFile.addEvent(event);
			}
		}
	}


	public boolean hasListeners(String channel) {
		List<Listener> list = map.get(channel);
		return (list != null) && !list.isEmpty();
	}
}
